﻿using System;
using System.Collections.Generic;
using System.IO;

namespace NetAssist.IO
{
  /// <summary>
  /// Provide methods that enumerate text file content yielding text blocks as they are requested. 
  /// </summary>
  public static class TextFile
  {
    /// <summary>
    /// Enumerate a the text file at the provided path reading fixed size blocks.
    /// </summary>
    /// <param name="path">Path of the file to enumerate.</param>
    /// <param name="blockSize">The read block size.</param>
    /// <returns>An IEnumerable&lt;TextFileBlock&gt; that enables reading the file in blocks.</returns>
    /// <remarks>Last block in the enumeration might have less data than the blockSize.</remarks>
    public static IEnumerable<TextFileBlock> ReadFile(string path, int blockSize)
    {
      using (StreamReader file = File.OpenText(path))
        foreach (TextFileBlock block in file.ReadBlocks(blockSize))
          yield return block;
    }

    /// <summary>
    /// Enumerate a the file at the provided path reading fixed size blocks.
    /// </summary>
    /// <param name="file">The stream reader to use for the enumeration.</param>
    /// <param name="blockSize">The read block size.</param>
    /// <returns>An IEnumerable&lt;TextFileBlock&gt; that enables reading the file in blocks.</returns>
    /// <remarks>Last block in the enumeration might have less data than the blockSize.</remarks>
    public static IEnumerable<TextFileBlock> ReadBlocks(this StreamReader file, int blockSize)
    {
      int index = 0;
      int offset = 0;
      int read = 0;
      char[] arr = new char[blockSize];

      do
      {
        read = file.Read(arr, 0, blockSize);
        if (read == blockSize)
          yield return new TextFileBlock(index, offset, new string(arr));
        else
        {
          if (read != 0)
            yield return new TextFileBlock(index, offset, new string(arr).Substring(0, read));
          yield break;
        }
        offset += read;
        index++;
      } while (true);
    }

    /// <summary>
    /// Enumerate a the text file at the provided path reading fixed size blocks.
    /// </summary>
    /// <param name="path">Path of the file to enumerate.</param>    
    /// <param name="read">A method that reads the file returning a string of the expected size.</param>
    /// <returns>An IEnumerable&lt;TextFileBlock&gt; that enables reading the file in blocks.</returns>
    /// <remarks>Last block in the enumeration might have less data than the blockSize.</remarks>           
    public static IEnumerable<TextFileBlock> ReadFile(string path, Func<StreamReader, string> read)
    {
      using (StreamReader file = File.OpenText(path))
        foreach (TextFileBlock block in file.ReadBlocks(read))
          yield return block;
    }

    /// <summary>
    /// Enumerate a the text file at the provided path reading fixed size blocks.
    /// </summary>
    /// <param name="file">The stream reader to use for the enumeration.</param>
    /// <param name="read">A method that reads the file returning a string of the expected size.</param>
    /// <returns>An IEnumerable&lt;TextFileBlock&gt; that enables reading the file in blocks.</returns>
    /// <remarks>Last block in the enumeration might have less data than the blockSize.</remarks>           
    public static IEnumerable<TextFileBlock> ReadBlocks(this StreamReader file, Func<StreamReader, string> read)
    {
      int index = 0;
      int offset = 0;
      do
      {
        string res = read(file);
        if (string.IsNullOrEmpty(res))
          yield break;

        yield return new TextFileBlock(index, offset, res);
        offset += res.Length;
        index++;
      } while (true);
    }
  }
}
